package problem.year2021.january;

/**
 * 用以太网线缆将 n 台计算机连接成一个网络，计算机的编号从 0 到 n-1。线缆用 connections 表示，其中 connections[i] = [a, b] 连接了计算机 a 和 b。
 * <p>
 * 网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。
 * <p>
 * 给你这个计算机网络的初始布线 connections，你可以拔开任意两台直连计算机之间的线缆，并用它连接一对未直连的计算机。
 * 请你计算并返回使所有计算机都连通所需的最少操作次数。如果不可能，则返回 -1 。
 * <p>
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/number-of-operations-to-make-network-connected
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */

public class January23 {
    static class Solution {
        public int makeConnected(int n, int[][] connections) {
            if (connections.length < n - 1) {
                return -1;
            }

            UnionFind uf = new UnionFind(n);
            for (int[] conn : connections) {
                uf.unite(conn[0], conn[1]);
            }
            return uf.setCount - 1;
        }
    }

    public static void main(String[] args) {
        // n = 6, connections = [[0,1],[0,2],[0,3],[1,2],[1,3]]
        System.out.println(new Solution().makeConnected(6, new int[][]{{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}}));
    }

    static class UnionFind {
        int setCount; // 记录所有联通分量数
        int[] parent; // 记录其父节点

        public UnionFind(int n) {
            this.setCount = n;
            parent = new int[n];
            for (int i = 0; i < n; ++i) {
                parent[i] = i;// 表示每个节点的父节点就是当前的自己，也就是说每个点现在是独立分开的
            }
        }

        private int findSet(int x) {
            return x == parent[x] ? x : findSet(parent[x]);
        }

        private boolean unite(int x, int y) { // 判断一下能不能合并
            x = findSet(x);
            y = findSet(y);
            if (x == y) {
                // 证明两个集合本来就是一个集合,不用合并
                return false;
            }
            // 合并两个集合
            parent[x] = parent[y];
            this.setCount -= 1;
            return true;
        }
    }
}
